package plugin.metrics;

import gui.MetricsResultWindow;

import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import models.action.Action;
import models.asl.Edge;
import models.asl.Graph;
import models.asl.Node;
import models.asl.NodeType;
import models.state.EnvStateGraph;
import models.state.State;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

import check.ActionAnalyser;

import reader.ActionReader;
import reader.TextEnvStateTransReader;
import reader.Transformation;
import reader.UMLReader;

public class MetricsHandler implements IObjectActionDelegate{
    private String filePath;
    public String projectPath;
    public String fileName;
    private ActionReader actionReader = new ActionReader();
    private UMLReader umlReader = new UMLReader();
    private TextEnvStateTransReader textEnvReader = new TextEnvStateTransReader();
    
    @Override
    public void run(IAction arg0) {
        try {
            List<Action> actions = actionReader.readActions(projectPath + File.separator + "temp" + File.separator + fileName + ".action");
            List<String> statesAgentCanChange = ActionAnalyser.findAgentEditableStates(actions);
            EnvStateGraph graph = null;
            try {
                graph = umlReader.parseUMLFile(projectPath + File.separator + "temp" + File.separator + "model.uml");
            } catch(Exception e) {
                graph = textEnvReader.readEnvStateTransDiagram(projectPath + File.separator + "temp" + File.separator + "model.txt");
            }
            List<State> states = graph.getStates();
            Set<String> externalState = new HashSet<String>();
            for(State state : states) {
                for(String sstate : state.getStates()) {
                    boolean found = false;
                    for(String agcState : statesAgentCanChange) {
                        if(sstate.equalsIgnoreCase(agcState)) {
                            found = true;
                            break;
                        }
                    }
                    //agent cannot change to this state
                    if(!found) {
                        externalState.add(sstate);
                    }
                    
                }
            }
            System.out.println("External State:" + externalState);
            Transformation transformation = new Transformation(filePath);
            List<Graph> domainGraph;
            domainGraph = transformation.generate();
            
            Double result = calculateReactivity(domainGraph.get(0), actions, externalState);
            DecimalFormat df = new DecimalFormat("#.##");
            arg0.setText("Measure Reactivity (" + df.format(result) + ")");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void selectionChanged(IAction arg0, ISelection arg1) {
        if(arg1 instanceof IStructuredSelection) {
            Object obj = ((IStructuredSelection) arg1).getFirstElement();
            IFile file = (IFile) Platform.getAdapterManager().getAdapter(obj,
                    IFile.class);
            if (file == null) {
                if (obj instanceof IAdaptable) {
                    file = (IFile) ((IAdaptable) obj).getAdapter(IFile.class);
                }
            }
            if(file != null) {
                filePath = file.getRawLocation().toOSString();
                fileName = file.getName();
                projectPath = file.getProject().getLocation().toOSString();
            }

        }
    }

    @Override
    public void setActivePart(IAction arg0, IWorkbenchPart arg1) {
        
    }
    
    private double calculateReactivity(Graph domainGraph, List<Action> actions, Set<String> externalState) {
        Map<Integer, Integer> goalDependentActionCountMap = new HashMap<Integer, Integer>();
        //find trigger node
        for(Node eachNode : domainGraph.getNodes()) {
            if(eachNode.getVisitedFlag() != null && eachNode.getVisitedFlag().equals(Boolean.FALSE)
                    && eachNode.getType() != null && eachNode.getType().equals(NodeType.TRIGGINGEVENT)) {
                //set to visited
                eachNode.setVisitedFlag(true);
                Node pNode = eachNode;
                DFSSearch(null,pNode, domainGraph, actions, externalState, goalDependentActionCountMap);
            }
        }
        double result = 1;
        for(Integer key : goalDependentActionCountMap.keySet()) {
        	result *= 1.0 /(goalDependentActionCountMap.get(key) + 1.0);
        }
//        MetricsResultWindow mrw = new MetricsResultWindow();
//        mrw.setValue(result);
//        mrw.setTitle("Reactivity for " + fileName);
//        mrw.setVisible(true);
        return result;
    }
    static int index = 0;
    
    private static void DFSSearch(Node preNode, Node pNode, Graph domainGraph, List<Action> actions,
    		Set<String> externalState, Map<Integer, Integer> goalDependentActionCountMap) {
            if(pNode.getType().equals(NodeType.ACTION)) {
                //if action has context condition infront, 
                //for action node, when there are multiple nodes
            	if(preNode != null &&  !preNode.getType().equals(NodeType.CONTEXT_CONDITION)) {
            		boolean foundFlag = false;
            		for(Action action : actions) {
            			if(action.getName().equalsIgnoreCase(pNode.getName())) {
            				for(String preCond : action.getPreCondition()) {
            					for(String extState : externalState) {
            						if(preCond.equalsIgnoreCase(extState)) {
            							foundFlag = true;
            						}
            					}
            				}
            			}
            		}
            		if(foundFlag) {
            			goalDependentActionCountMap.put(index,
            					goalDependentActionCountMap.get(index) + 1);
            		}
            	}
            } else if(pNode.getType().equals(NodeType.PLAN)) {
            	index++;
            	goalDependentActionCountMap.put(index, 0);
            }
            
            if(pNode.getOutgoingEdges() != null && !pNode.getOutgoingEdges().isEmpty()) {
                for(Edge oe : pNode.getOutgoingEdges()) {
                    Node nextNode = domainGraph.findNodeById(oe.getDestinationId());
                    DFSSearch(pNode, nextNode, domainGraph, actions, externalState, goalDependentActionCountMap);
                }
                  
            } else {
                return;
            }
    }
    
    
    private static List<String> calculateContextCondition(Set<State> currentState, List<Action> actions, Node nextAct) {
        List<String> contextCond = new ArrayList<String>();
        for(Action act : actions) {
            if(act.getName().equals(nextAct.getName())) {
                for(String preCond : act.getPreCondition()) {
                    //if preCond can change in states
                    int count = 0;
                    for(State state : currentState) {
                        for(String atomState : state.getStates()) {
                            if(preCond.equals(atomState)) {
                                count ++;
                            }
                        }
                     }
                    //if precondition exists but not same size as each state
                    if(count != 0 && count != currentState.size()) {
                        contextCond.add(preCond);
                    }
                }
                break;
            }
        }
        return contextCond;
    }
    
    
    
}
